package sundun.sharding.algorithm;

import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import sundun.sharding.constant.ShardingConstant;
import sundun.sharding.enums.ShardingTableCacheEnum;
import sundun.sharding.utils.ShardingAlgorithmUtil;
import sundun.sharding.utils.TimeFrameEndpointUtil;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * 分片格式： 表名_年份月份（ims_yesterday_jqxx_202407）
 * 分片算法，按月分片 分片字段为日期
 *
 * @author zxl
 * @date 2024/07/30 12:13
 */
@Slf4j
public class TimeShardingAlgorithm implements StandardShardingAlgorithm<Date> {



    /**
     * 精准分片
     * @param tableNames 对应分片库中所有分片表的集合
     * @param preciseShardingValue 分片键值，其中 logicTableName 为逻辑表，columnName 分片键，value 为从 SQL 中解析出来的分片键的值
     * @return 表名
     */
    @Override
    public String doSharding(Collection<String> tableNames, PreciseShardingValue<Date> preciseShardingValue) {
        String logicTableName = preciseShardingValue.getLogicTableName();
        ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
        ShardingAlgorithmUtil.createAllTable(logicTable, tableNames);
        if (logicTable == null) {
            log.error("【{}】数据表类型错误，请稍后重试，logicTableNames：{}，logicTableName:{}",
                    logicTableName,ShardingTableCacheEnum.logicTableNames(), logicTableName);
            throw new IllegalArgumentException("数据表类型错误，请稍后重试");
        }

        /// 打印分片信息
        log.info(" 【】精确分片，节点配置表名：{}，数据库缓存表名：{}",logicTableName, tableNames, logicTable.resultTableNamesCache());

        Date date = preciseShardingValue.getValue();
        Instant instant = date.toInstant();
        LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
        String resultTableName = logicTableName + "_" + ShardingConstant.TABLE_SHARD_TIME_FORMATTER.format(localDateTime);
        // 检查分表获取的表名是否存在，不存在则自动建表
        if (!tableNames.contains(resultTableName)){
            tableNames.add(resultTableName);
        }
        return ShardingAlgorithmUtil.getShardingTableAndCreate(logicTable, resultTableName);
    }

    /**
     * 范围分片
     * @param tableNames 对应分片库中所有分片表的集合
     * @param rangeShardingValue 分片范围
     * @return 表名集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<Date> rangeShardingValue) {
        Instant start = Instant.now();
        String logicTableName = rangeShardingValue.getLogicTableName();
        ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
        ShardingAlgorithmUtil.createAllTable(logicTable, tableNames);
        if (logicTable == null) {
            log.error("【{}】逻辑表范围异常，请稍后重试，logicTableNames：{}，logicTableName:{}",
                    logicTableName,ShardingTableCacheEnum.logicTableNames(), logicTableName);
            throw new IllegalArgumentException("逻辑表范围异常，请稍后重试");
        }
        // 打印分片信息
        log.info("【{}】范围分片，节点配置表名：{}，数据库缓存表名：{}", logicTableName,tableNames, logicTable.resultTableNamesCache());
        // between and 的起始值
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        boolean hasLowerBound = valueRange.hasLowerBound();
        boolean hasUpperBound = valueRange.hasUpperBound();

        // 获取最大值和最小值
        Set<String> tableNameCache = logicTable.resultTableNamesCache();
        String min = hasLowerBound ? String.valueOf(valueRange.lowerEndpoint()) : TimeFrameEndpointUtil.getLowerEndpointMonth(tableNameCache);
        String max = hasUpperBound ? String.valueOf(valueRange.upperEndpoint()) : TimeFrameEndpointUtil.getLowerEndpointMonth(tableNameCache);
        // 循环计算分表范围
        Set<String> resultTableNames = new LinkedHashSet<>();
        try {
            Date minDate = ShardingConstant.DATE_TIME_FORMATTER_SPILE.parse(min);
            Date maxDate = ShardingConstant.DATE_TIME_FORMATTER_SPILE.parse(max);
            Calendar calendar = Calendar.getInstance();
            while (minDate.before(maxDate) || minDate.equals(maxDate)) {
                String tableName = logicTableName + ShardingConstant.TABLE_SPLIT_SYMBOL + ShardingConstant.TABLE_SHARD_DATE_FORMATTER.format(minDate);
                resultTableNames.add(tableName);
                // 设置Calendar的时间为Date对象的时间
                calendar.setTime(minDate);
                // 给日期加一天
                calendar.add(Calendar.DAY_OF_MONTH, 1);
                minDate = calendar.getTime();
            }
            Instant end = Instant.now();
            log.info("【{}】分表查询结束耗时:{}ms",logicTableName, Duration.between(start, end).toMillis());
            return ShardingAlgorithmUtil.getShardingTablesAndCreate(logicTable, resultTableNames);
        } catch (Exception e) {
            return ShardingAlgorithmUtil.getShardingTablesAndCreate(logicTable, logicTable.resultTableNamesCache());
        }
    }


    @Override
    public void init() {

    }

    @Override
    public String getType() {
        return null;
    }



}
